package com.wispx.shortlink.admin.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wispx.shortlink.admin.common.biz.user.UserContext;
import com.wispx.shortlink.admin.common.convention.exception.ClientException;
import com.wispx.shortlink.admin.dao.entity.GroupDO;
import com.wispx.shortlink.admin.dao.mapper.GroupMapper;
import com.wispx.shortlink.admin.dto.req.ShortLinkGroupSortReqDTO;
import com.wispx.shortlink.admin.dto.req.ShortLinkGroupUpdateReqDTO;
import com.wispx.shortlink.admin.dto.resp.ShortLinkGroupRespDTO;
import com.wispx.shortlink.admin.remote.ShortLinkRemoteService;
import com.wispx.shortlink.admin.remote.dto.resp.ShortLinkCountQueryRespDTO;
import com.wispx.shortlink.admin.service.GroupService;
import com.wispx.shortlink.admin.toolkit.RandomStringGenerator;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static com.wispx.shortlink.admin.common.constant.RedisCacheConstant.LOCK_GROUP_CREATE_KEY;

/**
 * 短链接分组服务实现层
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class GroupServiceImpl extends ServiceImpl<GroupMapper, GroupDO> implements GroupService {

    private final RedissonClient redissonClient;
    private final ShortLinkRemoteService shortLinkRemoteService;

    @Value("${short-link.group-count.max}")
    private long groupMaxCount;

    boolean hasGid(String gid){
        return hasGid(UserContext.getUsername(),gid);
    }

    /**
     * gid 是否重复
     *
     * @param username 用户名
     * @param gid 短链接分组gid
     * @return true gid存在,false gid不存在
     */
    boolean hasGid(String username,String gid){
        LambdaQueryWrapper<GroupDO> queryWrapper = Wrappers.lambdaQuery(GroupDO.class)
                .eq(GroupDO::getGid, gid)
                .eq(GroupDO::getUsername, username);
        GroupDO result = baseMapper.selectOne(queryWrapper);
        return result != null;
    }

    @Override
    public void saveGroup(String groupName) {
        saveGroup(UserContext.getUsername(),groupName);
    }

    @Override
    public void saveGroup(String username, String groupName) {
        // 加锁为了防止同一用户在不同线程（可能同一用户在多个设备）同时新增分组，而超出最大分组数吧。
        RLock lock = redissonClient.getLock(LOCK_GROUP_CREATE_KEY + username);
        lock.lock();
        try {
            LambdaQueryWrapper<GroupDO> queryWrapper = Wrappers.lambdaQuery(GroupDO.class)
                    .eq(GroupDO::getUsername, username);
            Long groupCount = baseMapper.selectCount(queryWrapper);
            if(groupCount != null && groupCount == groupMaxCount){
                throw new ClientException("用户最多创建分组数量为" + groupMaxCount);
            }
            String gid = null;
            do {
                gid = RandomStringGenerator.generateRandomString();
            } while (hasGid(username,gid));
            GroupDO groupDO = GroupDO.builder()
                    .gid(gid)
                    .name(groupName)
                    .sortOrder(0)
                    .username(username)
                    .sortOrder(0)
                    .build();
            baseMapper.insert(groupDO);
        }finally {
            lock.unlock();
        }
    }

    @Override
    public List<ShortLinkGroupRespDTO> listGroup() {
        LambdaQueryWrapper<GroupDO> queryWrapper = Wrappers.lambdaQuery(GroupDO.class)
                // TODO 获取用户名
                // .isNull(GroupDO::getUsername)
                .eq(GroupDO::getUsername,UserContext.getUsername())
                .orderByDesc(GroupDO::getSortOrder, GroupDO::getUpdateTime);
        List<GroupDO> groupDOList = baseMapper.selectList(queryWrapper);
        List<String> gid = groupDOList.stream().map(GroupDO::getGid).toList();
        // 远程调用查询各组短链接数量
        List<ShortLinkCountQueryRespDTO> shortLinkCountList = shortLinkRemoteService.listShortLinkCount(gid).getData();
        List<ShortLinkGroupRespDTO> shortLinkGroupList = BeanUtil.copyToList(groupDOList, ShortLinkGroupRespDTO.class);
        Map<String, Integer> shortLinkCountMap = shortLinkCountList.stream().collect(Collectors.toMap(
                ShortLinkCountQueryRespDTO::getGid, //key
                ShortLinkCountQueryRespDTO::getShortLinkCount));//value
        shortLinkGroupList.forEach(each -> each.setShortLinkCount(shortLinkCountMap.get(each.getGid())));
        return shortLinkGroupList;
    }

    @Override
    public void updateGroup(ShortLinkGroupUpdateReqDTO requestParam) {
        LambdaUpdateWrapper<GroupDO> updateWrapper = Wrappers.lambdaUpdate(GroupDO.class)
                .eq(GroupDO::getGid, requestParam.getGid())
                .eq(GroupDO::getUsername, UserContext.getUsername());
        baseMapper.update(GroupDO.builder()
                        .name(requestParam.getName())
                        .build(),updateWrapper);
    }

    @Override
    public void deleteGroup(String gid) {
        LambdaUpdateWrapper<GroupDO> updateWrapper = Wrappers.lambdaUpdate(GroupDO.class)
                .eq(GroupDO::getGid, gid)
                .eq(GroupDO::getUsername, UserContext.getUsername());
        baseMapper.delete(updateWrapper);
    }

    @Override
    public void sortGroup(List<ShortLinkGroupSortReqDTO> requestParam) {
        requestParam.forEach((requestDTO) ->{
            GroupDO groupDO = GroupDO.builder()
                    .sortOrder(requestDTO.getSortOrder())
                    .build();
            LambdaUpdateWrapper<GroupDO> updateWrapper = Wrappers.lambdaUpdate(GroupDO.class)
                    .eq(GroupDO::getGid, requestDTO.getGid())
                    .eq(GroupDO::getUsername, UserContext.getUsername());
            baseMapper.update(groupDO,updateWrapper);
        } );
    }
}
